package com.cyj.dream.core.util.date;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Description: 日期处理工具类
 * @BelongsProject: DreamChardonnay
 * @BelongsPackage: com.cyj.dream.core.util.date
 * @Author: ChenYongJia
 * @CreateTime: 2021-09-26 11:26
 * @Email: chen87647213@163.com
 * @Version: 1.0
 */
public final class DateUtils {

    public static final String DEFAULT_DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
    public static final String DEFAULT_DATE_PATTERN = "yyyy-MM-dd";
    public static final String DATE_TIME_PATTERN = "MM-dd HH:mm";
    /**
     * 用于存放周一到周日
     */
    private static final Map<Integer, String> weekMap = new HashMap<>(16);
    private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    private static final DateFormat fullDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    /**
     * 默认的时间格式化
     **/
    private final static DateTimeFormatter DEFAULT_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_PATTERN);
    private final static DateTimeFormatter DEFAULT_DATE_FORMATTER = DateTimeFormatter.ofPattern(DEFAULT_DATE_PATTERN);
    /**
     * 时间格式化器缓存，DateTimeFormatter 是线程安全的，避免每次实例化一个对象
     **/
    private final static Map<String, DateTimeFormatter> DATE_TIME_FORMATTER_CACHE = new ConcurrentHashMap<>(16);

    /** 定义周一到周日 */
    static {
        weekMap.put(Calendar.SUNDAY, "日");
        weekMap.put(Calendar.MONDAY, "一");
        weekMap.put(Calendar.TUESDAY, "二");
        weekMap.put(Calendar.WEDNESDAY, "三");
        weekMap.put(Calendar.THURSDAY, "四");
        weekMap.put(Calendar.FRIDAY, "五");
        weekMap.put(Calendar.SATURDAY, "六");
    }

    private DateUtils() {
    }

    /**
     * 获取周信息：周一--周日
     *
     * @param week
     * @return
     */
    public static String getWeek(Integer week) {
        return weekMap.get(week);
    }

    /**
     * 使用<strong>默认</strong>的日期时间格式，将时间字符串转换成 LocalDateTime 对象
     *
     * @param dateTimeStr 时间字符串
     * @return LocalDateTime
     * @author li.guoqiang
     **/
    public static LocalDateTime parseForDateTime(String dateTimeStr) {
        if (dateTimeStr == null || "".equals(dateTimeStr)) {
            return null;
        }
        return parseForDateTime(dateTimeStr, DEFAULT_DATE_TIME_PATTERN);
    }

    /**
     * 使用<strong>指定</strong>的日期时间格式，将时间字符串转换成 LocalDateTime 对象
     *
     * @param dateTimeStr 时间字符串
     * @return LocalDateTime
     * @author li.guoqiang
     **/
    public static LocalDateTime parseForDateTime(String dateTimeStr, String pattern) {
        if (dateTimeStr == null || "".equals(dateTimeStr)) {
            return null;
        }
        DateTimeFormatter formatter = getDateTimeFormatter(pattern);
        return LocalDateTime.parse(dateTimeStr, formatter);
    }

    /**
     * 转换成localDate
     *
     * @param dateTimeStr
     * @return
     */
    public static LocalDate parseForLocalDate(String dateTimeStr) {
        if (dateTimeStr == null || "".equals(dateTimeStr)) {
            return null;
        }
        DateTimeFormatter formatter = getDateTimeFormatter(DEFAULT_DATE_PATTERN);
        return LocalDate.parse(dateTimeStr, formatter);
    }

    /**
     * 使用<strong>默认</strong>的日期时间格式，将日期时间格式化成字符串
     *
     * @param dateTime 日期时间对象
     * @return 日期时间字符串
     * @author li.guoqiang
     **/
    public static String formatToDateTime(LocalDateTime dateTime) {
        if (dateTime == null) {
            return null;
        }
        return DEFAULT_DATE_TIME_FORMATTER.format(dateTime);
    }

    public static String formatToDate(LocalDate dateTime) {
        if (dateTime == null) {
            return null;
        }
        return DEFAULT_DATE_FORMATTER.format(dateTime);
    }

    /**
     * 使用<strong>指定</strong>的日期时间格式，将日期时间格式化成字符串
     *
     * @param dateTime 日期时间对象
     * @return 日期时间字符串
     * @author li.guoqiang
     **/
    public static String formatToDateTime(LocalDateTime dateTime, String pattern) {
        if (dateTime == null) {
            return null;
        }
        DateTimeFormatter formatter = getDateTimeFormatter(pattern);
        return formatter.format(dateTime);
    }

    /**
     * 获取格式化器，DateTimeFormatter 是线程安全的，这里将其缓存
     *
     * @param pattern 格式
     * @return DateTimeFormatter
     * @author li.guoqiang
     **/
    private static DateTimeFormatter getDateTimeFormatter(String pattern) {
        DateTimeFormatter formatter = DATE_TIME_FORMATTER_CACHE.get(pattern);
        if (formatter == null) {
            formatter = DateTimeFormatter.ofPattern(pattern);
            DATE_TIME_FORMATTER_CACHE.put(pattern, formatter);
        }
        return formatter;
    }

    /**
     * 时间戳转localDateTime
     *
     * @param timeStamp
     * @return
     */
    public static LocalDateTime toLocalDateTime(Long timeStamp) {
        if (timeStamp == null) {
            return null;
        }
        return Instant.ofEpochMilli(timeStamp).atZone(ZoneOffset.ofHours(8)).toLocalDateTime();
    }

    /**
     * 时间戳转localDatetime
     *
     * @param dateTime
     * @return
     */
    public static Long toTimeStamp(LocalDateTime dateTime) {
        if (dateTime == null) {
            return null;
        }
        return dateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
    }

    /**
     * 按照参数format的格式，字符串转日期
     * <p>
     * 默认格式： yyyy-MM-dd HH:mm:ss
     *
     * @param str
     * @return
     */
    public static Date formatDate(String str) {
        return formatDate(str, DEFAULT_DATE_TIME_PATTERN);
    }

    /**
     * 按照参数format的格式，字符串转日期
     *
     * @param dateStr
     * @param dateFormat 例如：yyyy-MM-dd HH:mm:ss
     * @return
     */
    public static Date formatDate(String dateStr, String dateFormat) {
        SimpleDateFormat df = new SimpleDateFormat(dateFormat);
        Date date = null;
        try {
            date = df.parse(dateStr);
        } catch (ParseException e) {
            throw new RuntimeException(
                    String.format("String [%s] to Date by Format [%s] failure!", dateStr, dateFormat));
        }
        return date;
    }


    /**
     * @param startStr
     * @param endStr
     * @param format
     * @return
     * @Description 从开始日期至结束时间相差多分钟
     */
    public static long getMinutesDuration(String startStr, String endStr, String format) {
        Date startDate = formatDate(startStr, format);
        Date endDate = formatDate(endStr, format);
        return (endDate.getTime() - startDate.getTime()) / 60000;
    }

    /**
     * 获取指定日期，所在月，最小一天，最小时间
     *
     * @return
     */
    public static LocalDateTime getDateMonthMinTime(LocalDateTime date) {
        LocalDateTime firstDay = date.with(TemporalAdjusters.firstDayOfMonth());
        return firstDay.with(LocalTime.MIN);
    }

    /**
     * 获取指定日期，所在月，最大一天，最大时间
     *
     * @return
     */
    public static LocalDateTime getDateMonthMaxTime(LocalDateTime date) {
        LocalDateTime lastDay = date.with(TemporalAdjusters.lastDayOfMonth());
        return lastDay.with(LocalTime.MAX);
    }

    /**
     * 计算两个时间之间的时间差
     *
     * @param before 开始
     * @param after  结束
     * @return Duration 对象
     * @author li.guoqiang
     **/
    public static Duration duration(LocalDateTime before, LocalDateTime after) {
        if (before == null || after == null) {
            return null;
        }
        return Duration.between(before, after);
    }

    /**
     * 计算两个时间之间的 分钟差
     *
     * @param before 开始
     * @param after  结束
     * @return Duration 对象
     * @author li.guoqiang
     **/
    public static long durationByMinute(LocalDateTime before, LocalDateTime after) {
        Duration duration = duration(before, after);
        if (duration == null) {
            return -1;
        }
        return duration.toMinutes();
    }

    /**
     * 获取指定日期 最小
     *
     * @param date
     * @return
     */
    public static LocalDateTime getDateMaxTime(LocalDateTime date) {
        return date.with(LocalTime.MAX);
    }

    /**
     * 指定日期最大
     *
     * @param date
     * @return
     */
    public static LocalDateTime getDateMinTime(LocalDateTime date) {
        return date.with(LocalTime.MIN);
    }

    /**
     * 获取今天，最大时间 即 23:59:59
     *
     * @return LocalDateTime
     */
    public static LocalDateTime getTodayMaxTime() {
        return LocalDateTime.now().with(LocalTime.MAX);
    }

    /**
     * 获取今天，最小时间，即 00:00:00
     *
     * @return LocalDateTime
     */
    public static LocalDateTime getTodayMinTime() {
        return LocalDateTime.now().with(LocalTime.MIN);
    }

    /**
     * 获取今天，最小时间，即 00:00:00
     *
     * @return LocalDateTime
     */
    public static LocalDateTime getMaxTimeAfterDays(int days) {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime afterTime = now.plusDays(days);
        return afterTime.with(LocalTime.MAX);
    }

    /**
     * 获取今天所在月最大时间
     *
     * @return
     */
    public static LocalDateTime getNowMinMonthTime() {
        LocalDateTime firstDay = LocalDateTime.now().with(TemporalAdjusters.firstDayOfMonth());
        return firstDay.with(LocalTime.MIN);
    }

    /**
     * 获取今天所在月最大时间
     *
     * @return
     */
    public static LocalDateTime getNowMaxMonthTime() {
        LocalDateTime lastDay = LocalDateTime.now().with(TemporalAdjusters.lastDayOfMonth());
        return lastDay.with(LocalTime.MAX);
    }


    /**
     * @param date
     * @Description: 将 Date 类型的转为 LocalDateTime
     * @return: LocalDateTime
     * @Author: ChenYongJia
     * @CreateTime: 2020/12/13 14:46
     */
    public static LocalDateTime toLocalDateTime(Date date) {
        if (date == null) {
            return null;
        }
        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
        return localDateTime;
    }

    /**
     * @param date
     * @Description: 将 Date 类型的转为 localDateTime.toLocalDate();
     * @return: LocalDate
     * @Author: ChenYongJia
     * @CreateTime: 2020/12/13 14:46
     */
    public static LocalDate toLocalDate(Date date) {
        if (date == null) {
            return null;
        }
        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
        return localDateTime.toLocalDate();
    }

    /**
     * @param date
     * @Description: 将 Date 类型格式化为 "yyyy-MM-dd" 形式的字符串
     * @return: String
     * @Author: ChenYongJia
     * @CreateTime: 2020/12/13 14:47
     */
    public static String getDateFormatYYYYMMDD(Date date) {
        SimpleDateFormat sdf = new SimpleDateFormat(DEFAULT_DATE_PATTERN);
        String dateStr = sdf.format(date);
        return dateStr;
    }

    /**
     * 获取指定时间加days天
     *
     * @param date 指定时间
     * @param days 延后天数
     * @return
     */
    public static LocalDateTime getPlusDays(LocalDateTime date, Integer days) {
        return date.plusDays(days);
    }

    /**
     * @param
     * @Description: 格式化 new SimpleDateFormat("yyyy-MM-dd");
     * @return: DateFormat
     * @Author: ChenYongJia
     * @CreateTime: 2020/12/13 14:47
     */
    public static DateFormat getDateFormat() {
        return dateFormat;
    }

    /**
     * @param
     * @Description: 格式化 "yyyy-MM-dd HH:mm:ss"
     * @return: DateFormat
     * @Author: ChenYongJia
     * @CreateTime: 2020/12/13 14:47
     */
    public static DateFormat getFullDateFormat() {
        return fullDateFormat;
    }

    /**
     * 计算两个日期之间相差的天数
     *
     * @param startDate 较小的时间
     * @param endDate   较大的时间
     * @return
     */
    public static int daysBetween(Date startDate, Date endDate) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(startDate);
        long time1 = cal.getTimeInMillis();
        cal.setTime(endDate);
        long time2 = cal.getTimeInMillis();
        long betweenDays = (time2 - time1) / (1000 * 3600 * 24);
        return Integer.parseInt(String.valueOf(betweenDays));
    }

}
